using System;
using System.Reflection;
using NUnit.Framework;
using ValidationFramework.Reflection;

namespace ValidationFramework.Tests
{
    [TestFixture]
    public class MethodDescriptorTest
    {

        [TestFixture]
        public class EmptyInstanceMethod
        {

            public void TempMethod(int tempParam){}
            
            [Test]
            public void Test()
            {
                var handle = typeof(EmptyInstanceMethod).GetMethod("TempMethod").MethodHandle;
                var methodDescriptor = new MethodDescriptor(handle);
                Assert.AreEqual("TempMethod", methodDescriptor.Name);
                Assert.AreEqual(1, methodDescriptor.Parameters.Count);
                Assert.AreEqual(handle, methodDescriptor.RuntimeMethodHandle);
                Assert.IsFalse(methodDescriptor.IsStatic);
            }

        }
        [TestFixture]
        public class EmptyStaticMethod
        {

            public static void TempMethod(int tempParam){}

            [Test]
            public void Test()
            {
                var handle = typeof(EmptyStaticMethod).GetMethod("TempMethod").MethodHandle;
                var methodDescriptor = new MethodDescriptor(handle);
                Assert.AreEqual("TempMethod", methodDescriptor.Name);
                Assert.AreEqual(1, methodDescriptor.Parameters.Count);
                Assert.AreEqual(handle, methodDescriptor.RuntimeMethodHandle);
                Assert.IsTrue(methodDescriptor.IsStatic);
            }

        }
        [TestFixture]
        public class Construction
        {
            public string TempProperty{get;set;}

            [Test]
            [ExpectedException(typeof(ArgumentException))]
            public void ExceptionPropertyMethodConstructor()
            {
                var PropertyMethodHandle = typeof(Construction).GetProperty("TempProperty").GetGetMethod().MethodHandle;
                new MethodDescriptor(PropertyMethodHandle);
            }
        }
        [TestFixture]
        public class InheritenceWithOverrideVirtualMethod
        {
            public class TempInheritedClass : TempBaseClass
            {
                public override void TempMethod([LengthStringRule(2)]string tempParam) {  }
            }

            public class TempBaseClass
            {
                public virtual void TempMethod([RequiredStringRule]string tempParam){ }
            }

            [Test]
            public void Test()
            {
                MethodCache.Clear();

                var handle = typeof(TempInheritedClass).GetMethod("TempMethod").MethodHandle;
                var methodDescriptor = new MethodDescriptor(handle);
                Assert.AreEqual(handle, methodDescriptor.RuntimeMethodHandle);
                Assert.AreEqual(1, methodDescriptor.Parameters.Count);
                Assert.AreEqual(2, methodDescriptor.Parameters["tempParam"].Rules.Count);
            }

        }
        [TestFixture]
        public class InheritenceWithDifferentParamNameMethod
        {
            public class TempInheritedClass : TempBaseClass
            {
                public override void TempMethod([LengthStringRule(2)]string tempParam) {  }
            }

            public class TempBaseClass
            {
                public virtual void TempMethod([RequiredStringRule]string tempParam1){ }
            }

            [Test]
            public void Test()
            {
                MethodCache.Clear();

                var handle = typeof(TempInheritedClass).GetMethod("TempMethod").MethodHandle;
                var methodDescriptor = new MethodDescriptor(handle);
                Assert.AreEqual(handle, methodDescriptor.RuntimeMethodHandle);
                Assert.AreEqual(1, methodDescriptor.Parameters.Count);
                Assert.AreEqual(2, methodDescriptor.Parameters["tempParam"].Rules.Count);
            }

        }
        [TestFixture]
        public class InheritenceWithOverrideAbstractMethod
        {
            public class TempInheritedClass : TempBaseClass
            {
                public override void TempMethod([LengthStringRule(2)]string tempParam) {  }
            }

            public abstract class TempBaseClass
            {
                public abstract void TempMethod([RequiredStringRule] string tempParam);
            }

            [Test]
            public void Test()
            {
                MethodCache.Clear();

                var handle = typeof(TempInheritedClass).GetMethod("TempMethod").MethodHandle;
                var methodDescriptor = new MethodDescriptor(handle);
                Assert.AreEqual(handle, methodDescriptor.RuntimeMethodHandle);
                Assert.AreEqual(1, methodDescriptor.Parameters.Count);
                Assert.AreEqual(2, methodDescriptor.Parameters["tempParam"].Rules.Count);
            }

        }
        [TestFixture]
        public class InterfaceMethod
        {
            public class TempInheritedClass : TempInterface
            {
                public void TempMethod([LengthStringRule(2)]string tempParam) {  }
            }

            public interface TempInterface
            {
                void TempMethod([RequiredStringRule] string tempParam);
            }

            [Test]
            public void Test()
            {
                MethodCache.Clear();

                var handle = typeof(TempInheritedClass).GetMethod("TempMethod").MethodHandle;
                var methodDescriptor = new MethodDescriptor(handle);
                Assert.AreEqual(handle, methodDescriptor.RuntimeMethodHandle);
                Assert.AreEqual(1, methodDescriptor.Parameters.Count);
                Assert.AreEqual(2, methodDescriptor.Parameters["tempParam"].Rules.Count);
            }

        }
        [TestFixture]
        public class InterfaceWithDifferentParamNameMethod
        {
            public class TempInheritedClass : TempInterface
            {
                public void TempMethod([LengthStringRule(2)]string tempParam) {  }
            }

            public interface TempInterface
            {
                void TempMethod([RequiredStringRule] string tempParam2);
            }

            [Test]
            public void Test()
            {
                MethodCache.Clear();

                var handle = typeof(TempInheritedClass).GetMethod("TempMethod").MethodHandle;
                var methodDescriptor = new MethodDescriptor(handle);
                Assert.AreEqual(handle, methodDescriptor.RuntimeMethodHandle);
                Assert.AreEqual(1, methodDescriptor.Parameters.Count);
                Assert.AreEqual(2, methodDescriptor.Parameters["tempParam"].Rules.Count);
            }

        }
        [TestFixture]
        public class ExplicitInterfaceMethod
        {
            public class TempInheritedClass : TempInterface
            {
                void TempInterface.TempMethod([LengthStringRule(2)]string tempParam) { }
            }

            public interface TempInterface
            {
                void TempMethod([RequiredStringRule] string tempParam);
            }

            [Test]
            public void Test()
            {
                MethodCache.Clear();
                var handle = typeof(TempInheritedClass).GetMethod("ValidationFramework.Tests.MethodDescriptorTest.ExplicitInterfaceMethod.TempInterface.TempMethod", BindingFlags.NonPublic | BindingFlags.Instance).MethodHandle;
                var methodDescriptor = new MethodDescriptor(handle);
                Assert.AreEqual(handle, methodDescriptor.RuntimeMethodHandle);
                Assert.AreEqual(1, methodDescriptor.Parameters.Count);
                Assert.AreEqual(2, methodDescriptor.Parameters["tempParam"].Rules.Count);
            }

        }
        [TestFixture]
        public class InheritenceWithNewMethod
        {
            public class TempInheritedClass : TempBaseClass
            {
               public new void TempMethod( [LengthStringRule(2)]string tempParam) { }
            }
            public class TempBaseClass
            {
                public virtual void TempMethod([RequiredStringRule]string tempParam) { }
            }
            [Test]
            public void Test()
            {
                //TODO: not sure if this is the correct behaviour??
                MethodCache.Clear();
                var handle = typeof(TempInheritedClass).GetMethod("TempMethod").MethodHandle;
                var methodDescriptor = new MethodDescriptor(handle);
                Assert.AreEqual(handle, methodDescriptor.RuntimeMethodHandle);
                Assert.AreEqual(1, methodDescriptor.Parameters.Count);
                Assert.AreEqual(1, methodDescriptor.Parameters["tempParam"].Rules.Count);
            }

        }
        [TestFixture]
        public class OutParamMethod
        {
            public class TempClass 
            {
               public void TempMethod( [RequiredStringRule] out string tempParam) 
               {
                   tempParam = null;
               }
            }

            [Test]
            [ExpectedException(typeof(ArgumentException))]
            public void Test()
            {
                MethodCache.Clear();
                var handle = typeof(TempClass).GetMethod("TempMethod").MethodHandle;
                new MethodDescriptor(handle);
            }

        }
        [TestFixture]
        public class RefParamMethod
        {
            public class TempClass 
            {
               public void TempMethod( [RequiredStringRule] ref string tempParam) 
               {
                   tempParam = null;
               }
            }

            [Test]
            public void Test()
            {
                MethodCache.Clear();
                var handle = typeof(TempClass).GetMethod("TempMethod").MethodHandle;
                var methodDescriptor = new MethodDescriptor(handle);
                Assert.AreEqual(handle, methodDescriptor.RuntimeMethodHandle);
                Assert.AreEqual(1, methodDescriptor.Parameters.Count);
                Assert.AreEqual(1, methodDescriptor.Parameters["tempParam"].Rules.Count);
            }

        }
        [TestFixture]
        public class ItemMethod
        {
            public class TempClass
            {
                public string this[[RequiredIntRule]int tempParam]
                {
                    get
                    {
                        return null;
                    }
                    set
                    {
                    }
                }
            }

            [Test]
            public void Test()
            {
                MethodCache.Clear();
                var handle = typeof(TempClass).GetMethod("get_Item").MethodHandle;
                var methodDescriptor = new MethodDescriptor(handle);
                Assert.AreEqual(handle, methodDescriptor.RuntimeMethodHandle);
                Assert.AreEqual(1, methodDescriptor.Parameters.Count);
                Assert.AreEqual(1, methodDescriptor.Parameters["tempParam"].Rules.Count);
            }

        }
        [TestFixture]
        public class GenericMethod
        {
            public class TempClass
            {
            }

            public class TempBaseClass<T>
            {
                public void TempMethod(T tempParam){}
            }

            [Test]
            [Ignore]
            public void Test()
            {
                MethodCache.Clear();
                var handle = typeof(TempClass).GetMethod("TempMethod").MethodHandle;
                var methodDescriptor = new MethodDescriptor(handle);
                Assert.AreEqual(handle, methodDescriptor.RuntimeMethodHandle);
                Assert.AreEqual(1, methodDescriptor.Parameters.Count);
                //TODO: support generics
             //   Assert.AreEqual(1, methodDescriptor.Parameters["tempParam"].Rules.Count);
            }

        }
    }
}